import datetime
import requests
import time
import threading
import redis

from datetime import timedelta

from exts import db
from configs import *
from flask_cors import CORS
from flask import Response, jsonify, Flask, redirect, url_for, request, flash, session, make_response
from flask_sqlalchemy import SQLAlchemy

# 蓝图导入
from Blueprint.Image_blue import imageblue
from Blueprint.user_view import user_view
from Blueprint.commands import cli  # 预置数据导入
from Blueprint.model_view import model_view
from Blueprint.menu_view import menu_view
from Blueprint.model_views import model_views
from Blueprint.scheduler_task import scheduler_task,run_task
from Blueprint.activate import activate
from Blueprint.role_view import role_view
from flask_apscheduler import APScheduler
from flask_admin import Admin
from flask_admin.contrib.sqla import ModelView
from flask_babel import Babel
from flask_session import Session


# --------建立flask实例--------
app = Flask(__name__)

# --------注册CORS, "/*" 允许访问所有api--------
CORS(app, resources=r'/*',supports_credentials=True)

app.config.update(
    SESSION_COOKIE_SAMESITE='None',
    SESSION_COOKIE_SECURE=True
)

# --------获取 Flask 应用的根路径--------
root_path = app.root_path

# SQLALCHEMY_TRACK_MODIFICATIONS: 这个选项用于启用或禁用 SQLAlchemy 对象修改的追踪功能。
# True: 启用追踪。当数据库会话中的对象被修改时，SQLAlchemy 会发出信号。这在某些情况下可能会有用，例如，你需要在对象修改时执行某些操作。
# False: 禁用追踪。这是默认的推荐设置。禁用后，可以减少内存开销，因为追踪对象修改需要额外的内存
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

# --------第一种登录认证--------配置 Flask-Session 及login会话--------
# 设置 login-secret_key
app.secret_key = 'my_secret_key_login'  # 替换为你的密钥
# 设置会话超时时间为30分钟
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(minutes=15)
app.config['SESSION_TYPE'] = 'redis'  # 可以使用 'redis', 'filesystem', 'mongodb', 等
app.config['SESSION_PERMANENT'] = True
# 配置 Redis 连接
app.config['SESSION_REDIS'] = redis.Redis(host=Redis_ip, port=6379, password=None,db=0)  # 使用你的 Redis 主机、端口和密码
Session(app)

# 设置login初始化组件
# login_manager = LoginManager()
# login_manager.init_app(app)
# login_manager.login_view = 'login'  # 指定未登录时重定向的视图函数


# --------第二种登录认证--------初始化 JWTManager--------
jwt = JWTManager(app)
# 过期时间
# app.config['JWT_ACCESS_TOKEN_EXPIRES'] = timedelta(hours=24)
app.config['JWT_ACCESS_TOKEN_EXPIRES'] = timedelta(hours=12)
# 配置密钥
app.config['JWT_SECRET_KEY'] = 'my_secret_key'
# 自定义过期错误消息
@jwt.expired_token_loader
def expired_token_callback(jwt_header, jwt_payload):
    return jsonify({
        'code': 401,
        'msg': '令牌已过期，请重新登录！'
    })

# --------解决中文乱码--------
app.config['JSON_AS_ASCII'] = False

# --------定时任务配置--------
# 配置 APScheduler
class Config_APScheduler:
    SCHEDULER_API_ENABLED = True
app.config.from_object(Config_APScheduler)

# 初始化 APScheduler
scheduler = APScheduler()
scheduler.init_app(app)
scheduler.start()

# --------在应用对象上注册蓝图--------
app.register_blueprint(imageblue, url_prefix='/imageblue')
app.register_blueprint(user_view, url_prefix='/user_view') # 用户蓝图
app.register_blueprint(cli) # 预置数据蓝图
app.register_blueprint(model_view,url_prefix='/model_view') # 数据操作蓝图
app.register_blueprint(menu_view,url_prefix='/menu_view')  # 菜单蓝图
app.register_blueprint(model_views,url_prefix='/model_views') # 新增数据操作蓝图1
app.register_blueprint(scheduler_task,url_prefix='/scheduler_task') # 定时任务蓝图
app.register_blueprint(activate,url_prefix='/activate') # 激活序列蓝图
app.register_blueprint(role_view,url_prefix='/role_view') # 激活序列蓝图

# --------连接数据库--------
app.config.from_object(Config)

# --------初始化--------
db.init_app(app)

# --------绑定app和数据库--------
migrate = Migrate(app,db)

# --------后台管理页面 （添加表操作，进行后台管理）--------
babel = Babel(app)
admin = Admin(app, name='My_admin', template_mode='bootstrap3')
admin.add_view(ModelView(Mine, db.session))


@app.route('/', methods=['GET', 'POST', 'DELETE', 'PUT', 'CATCH'])
def def1():
    print('启动成功！')
    return jsonify({'code': 200})


# --------录像机设备信息定时同步--------
# 先查找录像机父表是否有数据，如果有数据循环读取（录像机ip），
# 用录像机ip去调用isapi来获取摄像头实时信息进行修改，如果不存在则进行添加操作
def vcr_update_task():  # 运行的定时任务的函数
    with app.app_context():
        # 查找录像机信息
        vcr_dict_list = convert_folder_to_dict_list(db.session.query(VCR_data).all(),['id','vcr_ip','vcr_username','vcr_password','vcr_port','Mine_id'])
        # 当录像机设备存在时执行逻辑
        if vcr_dict_list:
            # 因为录像机ip是多个，所以要进行循环获取每一个录像机ip
            for vcr in vcr_dict_list:

                # 获取父亲的id
                db_equipment_parent_id = db.session.query(Equipment).filter(Equipment.VCR_data_id == vcr['id'],
                                                                       Equipment.parent_id == None).first()

                # 获取外部设备信息
                external_data_list = VCR_data_info(vcr['vcr_username'], vcr['vcr_password'], vcr['vcr_ip'],
                                                   vcr['vcr_port'])

                if external_data_list == []:
                    print('当前录像机数据不存在或账密错误！')
                else:
                    # 获取外部设备信息列表,用于查询数据表存在矿上不存在删除操作
                    external_data_ip_list = [ i.get('ip_address') for i in external_data_list]

                    # 从数据库中获取设备信息并转换为字典
                    db_equipment_list = db.session.query(Equipment).filter(Equipment.VCR_data_id == vcr['id'],
                                                                           Equipment.parent_id != None).all()


                    # 如果db_equipment_list查到的数据不在external_data_list里，进行db_equipment_list数据的删除操作
                    for i in db_equipment_list:
                        if i.equipment_ip not in external_data_ip_list:
                            db.session.delete(i)
                    db.session.commit()

                    # 转换为字典
                    db_equipment_dict = {device.equipment_ip: device for device in db_equipment_list}

                    # print(external_data_list,'获取的外部信息')
                    # 循环isapi信息

                    for i in external_data_list:
                        ip_address = i['ip_address']
                        name = i['name']
                        id = i['id']
                        online = 1 if i['online'] == 'true' else 0
                        equipment_width_height = (
                                i.get('videoResolutionWidth', '') + 'x' + i.get('videoResolutionHeight', '')) if (i.get(
                            'videoResolutionWidth', '') + 'x' + i.get('videoResolutionHeight', '')) != 'x' else ''
                        videoCodecType = 'H265' if i.get('videoCodecType', '') == 'H.265' else 'H264'
                        constantBitRate = i['constantBitRate']
                        maxFrameRate = i['maxFrameRate']
                        # 当isapi信息存在于数据库中进行修改
                        if ip_address in db_equipment_dict:
                            # 如果存在，检查并更新信息
                            db_device = db_equipment_dict[ip_address]
                            db_device.equipment_name = name
                            db_device.online = online
                            db_device.equipment_ip = ip_address
                            db_device.equipment_aisles = id
                            db_device.code = str(id) + '01',
                            db_device.equipment_width_height = equipment_width_height
                            db_device.equipment_codetype = videoCodecType
                            db_device.equipment_constantBitRate = constantBitRate
                            db_device.equipment_maxFrameRate = maxFrameRate
                            db.session.commit()


                        else:
                            # 如果不存在，添加新设备
                            print(f"添加新设备: {i['name']}")
                            new_device = Equipment(
                                VCR_data_id=vcr['id'],
                                parent_id=db_equipment_parent_id.id,
                                equipment_aisles=id,  # 使用外部数据中的字段作为 equipment_aisles
                                equipment_name=name,
                                equipment_ip=ip_address,
                                online=online,
                                code = str(id)+'01',
                                equipment_type='录像机',
                                manufacturer_type='海康',
                                equipment_uname=vcr['vcr_username'],
                                equipment_password=vcr['vcr_password'],
                                user_status='1',
                                create_time=datetime.now(),
                                Mine_id=vcr['Mine_id'],
                                equipment_width_height=(vcr.get('videoResolutionWidth','')+'x'+vcr.get('videoResolutionHeight','')) if (vcr.get('videoResolutionWidth','')+'x'+vcr.get('videoResolutionHeight','')) !='x' else '',
                                equipment_codetype='H265' if vcr['videoCodecType'] == 'H.265' else 'H264',

                            )
                            db.session.add(new_device)
                            db.session.commit()
            print('录像机设备定时同步更新成功!')
        else:
                print('录像机设备不存在！')

scheduler.add_job(func=vcr_update_task, args=[], id="vcr_update_task_1", trigger="interval", minutes=30, replace_existing=True)
# scheduler.add_job(func=vcr_update_task, args=[], id="vcr_update_task_1", trigger="interval", seconds=10, replace_existing=True)

#算法状态监控--正常，异常
def monitorPoint_updata_task():
    print('-------------------------------------------算法状态监控-------------------------------------------')
    with app.app_context():
        # 获取设备信息和对应的算法配置
        equipment_data = db.session.query(Equipment.id, Equipment.equipment_ip, Algorithm_config.id,
                                          Algorithm_config.status).join(
            Algorithm_config, Algorithm_config.Equipment_id == Equipment.id).filter(Algorithm_config.status == 1).all()

        # 提取设备IP并去重
        equipment_ips = {item.equipment_ip for item in equipment_data}

        # 获取设备的在线状态
        equipment_ips_status_data = getDevRunStatus(list(equipment_ips))

        # 构建IP到状态的映射
        ip_status_map = {item.get('ip'): item.get('status') for item in equipment_ips_status_data}
        print(ip_status_map)
        # 更新模型状态
        for equipment_id, equipment_ip, config_id, _ in equipment_data:
            if equipment_ip in ip_status_map:
                status = ip_status_map[equipment_ip]
                # 批量更新数据库
                db.session.query(Algorithm_config).filter(Algorithm_config.id == config_id).update(
                    {'model_status': status})

        # 提交修改
        db.session.commit()


scheduler.add_job(func=monitorPoint_updata_task, args=[], id="monitorPoint_updata_task_1", trigger="interval", minutes=10, replace_existing=True)


# --------定时任务初始化--------
def setup_scheduler_tasks():
    try:
        with app.app_context():
            # 查询数据库获取所有定时任务配置
            tasks = db.session.query(ScheduledTask).all()
            if tasks:
                tasks_data = convert_folder_to_dict_list(tasks,['id','name','interval_seconds','time_type',
                                                                'rtsp_list','diagnosis_type_list',
                                                                'scheduled_concent','scheduled_status',
                                                                ])
                for task in tasks_data:
                    job_id = f"task_{task['id']}"
                    name = f"task_{task['name']}"
                    interval_seconds = int(task['interval_seconds'])
                    time_type = task['time_type']
                    rtsp_list =task['rtsp_list']
                    diagnosis_type_list = task['diagnosis_type_list']
                    scheduled_concent = task['scheduled_concent']
                    scheduled_status = str(task['scheduled_status'])

                    #当任务类型为启用的时候运行
                    if int(scheduled_status) == 1:

                        if time_type == 'seconds':
                            # 添加定时任务到 APScheduler
                            scheduler.add_job(
                                id=job_id,
                                func=run_task,
                                args=[job_id, name, interval_seconds,time_type,rtsp_list,diagnosis_type_list,scheduled_concent,scheduled_status],
                                trigger='interval',
                                seconds=interval_seconds,
                            )

                        elif time_type == 'minutes':
                            # 添加定时任务到 APScheduler
                            scheduler.add_job(
                                id=job_id,
                                func=run_task,
                                args=[job_id, name, interval_seconds,time_type,rtsp_list,diagnosis_type_list,scheduled_concent,scheduled_status],
                                trigger='interval',
                                minutes=interval_seconds,
                            )

                        elif time_type == 'hours':
                            # 添加定时任务到 APScheduler
                            scheduler.add_job(
                                id=job_id,
                                func=run_task,
                                args=[job_id, name, interval_seconds,time_type,rtsp_list,diagnosis_type_list,scheduled_concent,scheduled_status],
                                trigger='interval',
                                hours=interval_seconds,
                            )

                        elif time_type == 'days':
                            # 添加定时任务到 APScheduler
                            scheduler.add_job(
                                id=job_id,
                                func=run_task,
                                args=[job_id, name, interval_seconds,time_type,rtsp_list,diagnosis_type_list,scheduled_concent,scheduled_status],
                                trigger='interval',
                                days=interval_seconds,
                            )

                        elif time_type == 'weeks':
                            # 添加定时任务到 APScheduler
                            scheduler.add_job(
                                id=job_id,
                                func=run_task,
                                args=[job_id, name, interval_seconds,time_type,rtsp_list,diagnosis_type_list,scheduled_concent,scheduled_status],
                                trigger='interval',
                                weeks=interval_seconds,
                            )

                        print(f"已添加定时任务: {name}，触发类型: {time_type}   触发间隔{interval_seconds} ")

                    else:
                        print('任务状态禁用！')
            else:
                print('暂无定时任务!')

    except Exception as e:
        print(f"初始化定时任务时发生异常: {e}")


# # 每次接口请求刷新会话时间，如果15分钟内不存在接口请求-退出
# @app.before_request
# def before_request():
#     session.permanent = True  # 刷新会话过期时间
#     session.modified = True  # 确保会话被标记为已修改


# # 存储userid
# @login_manager.user_loader
# def load_user(user_id):
#     return User.query.get(int(user_id))
#
#
#
# @app.route('/test', methods=['GET'])
# @login_required
# def test():
#     return jsonify({'code':200,'msg':'成功'})

# # 登录视图
# @app.route('/login', methods=['GET'])
# def login():
#     username = request.args.get('username', None)
#     password = request.args.get('password', None)
#     # 去除用户名和密码中的首尾空格
#     username = username.strip() if username else None
#     password = password.strip() if password else None
#
#     if username is None or password is None:
#         return jsonify({"msg": "没有权限",'code':400})
#
#     # 尝试查询用户信息  (当没有数据的时候返回值为 None)
#     user_info = db.session.query(User).filter(User.username == username).first()
#
#     if user_info is None:
#         return jsonify({"msg": "用户名或密码错误",'code':401})
#
#     if user_info.user_status == '0':
#         return jsonify({"msg": "账号禁用！", 'code': 401})
#
#     # 如果用户名存在验证密码
#     if user_info and password_decryption(password,user_info.password):
#
#         # 最后登录时间修改
#         user_info.end_login_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
#         db.session.commit()
#
#         # 存储登录信息
#         login_user(user_info)
#         print(login_user(user_info),'session信息')
#         session['user_id'] = user_info.id  # 存储用户ID到会话
#
#         # 打印会话内容
#         current_session = session
#
#         # token返回
#         # access_token = create_access_token(identity=username)
#         return jsonify({
#             'code':200,
#             'msg':'登录成功！',
#             "session": {
#                 "user_id": session.get('user_id'),
#                 "permanent": session.permanent,
#                 "fresh": session.get('_fresh'),
#                 "id": session.get('_id')
#             }
#             # 'access_token': access_token,
#         })
#     else:
#         # 登录失败，返回错误响应
#         return jsonify({"msg" :"用户名或密码错误",'code': 401})
#
#
# # 注销视图
# @app.route('/logout')
# @login_required
# def logout():
#     logout_user()
#     return jsonify({'message': '用户注销成功'}), 200


# --------调用实例对象--------
if __name__ == '__main__':
    setup_scheduler_tasks()
    app.run(debug=False, host='0.0.0.0', port=5000)


